﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using HtmlAgilityPack;
using MusicExplorer.Data;
using MusicExplorer.WebRepository.Interface;
using MusicExplorer.WebRepository.Service;

namespace MusicExplorer.WebRepository
{
    public class TrackitdownWebRepository : WebRepositoryBase,
        IGroupTrackListingWebRepository, IGroupTopTrackListingWebRepository, IGroupFilterWebRepository,
        IArtistFilterWebRepository, IArtistSearchWebRepository, IArtistTrackListingWebRepository, IArtistTopTrackListingWebRepository
    {
        private const string m_url_GENRES = "http://www.trackitdown.net/";
        private const string m_url_TOPTRACKSFROMGENRE = "http://www.trackitdown.net/genre/{0}/top10.html?offset={1}";
        private const string m_url_TRACKSFROMGENRE = "http://www.trackitdown.net/genre/{0}/new_releases.html?offset={1}";
        private const string m_url_SEARCHARTISTS = "http://www.trackitdown.net/search/advanced?searchType=advanced&artist={0}&offset={1}";
        private const string m_url_TRACKSFROMARTIST = "http://www.trackitdown.net/artist/show_track_list/{0}?order=release_date&offset={1}";
        private const string m_url_TOPTRACKSFROMARTIST = "http://www.trackitdown.net/artist/show_track_list/{0}?order=rating&offset={1}";

        public TrackitdownWebRepository()
        {
            m_web_repository = ServiceWebRepository.GetWebRepository(ServiceWebRepository.GetTypeWebRepository(TypeWebRepository.MusicStore), this.GetType().Name);
            m_result_per_page = 20;
        }

        #region TRACKS
        public async Task<List<TRACK>> GetTracksFromGroupAsync(WebClient webClient, CancellationToken token, GROUP group, int page)
        {
            if (page == 1) m_nothingToLoad = false;
            List<TRACK> tracks = new List<TRACK>();
            if (!m_nothingToLoad)
            {
                HtmlDocument doc = new HtmlDocument();
                token.ThrowIfCancellationRequested();
                doc.LoadHtml(await ServiceWebRepository.GetUrlDataAsync(webClient, string.Format(m_url_TRACKSFROMGENRE, group.group_key, (page - 1) * m_result_per_page)));
                token.ThrowIfCancellationRequested();
                tracks = GetTracks(doc, token);
            }
            return tracks;
        }

        public async Task<List<TRACK>> GetTopTracksFromGroupAsync(WebClient webClient, CancellationToken token, GROUP group, int page)
        {
            if (page == 1) m_nothingToLoad = false;
            List<TRACK> tracks = new List<TRACK>();
            if (!m_nothingToLoad)
            {
                HtmlDocument doc = new HtmlDocument();
                token.ThrowIfCancellationRequested();
                doc.LoadHtml(await ServiceWebRepository.GetUrlDataAsync(webClient, string.Format(m_url_TOPTRACKSFROMGENRE, group.group_key, (page - 1) * m_result_per_page)));
                token.ThrowIfCancellationRequested();
                tracks = GetTracks(doc, token);
            }
            return tracks;
        }

        public async Task<List<TRACK>> GetTracksFromArtistAsync(WebClient webClient, CancellationToken token, ARTIST artist, int page)
        {
            if (page == 1) m_nothingToLoad = false;
            List<TRACK> tracks = new List<TRACK>();
            if (!m_nothingToLoad)
            {
                HtmlDocument doc = new HtmlDocument();
                token.ThrowIfCancellationRequested();
                doc.LoadHtml(await ServiceWebRepository.GetUrlDataAsync(webClient, string.Format(m_url_TRACKSFROMARTIST, artist.artist_key, (page - 1) * m_result_per_page)));
                token.ThrowIfCancellationRequested();
                tracks = GetTracks(doc, token);
            }
            return tracks;
        }


        public async Task<List<TRACK>> GetTopTracksFromArtistAsync(WebClient webClient, CancellationToken token, ARTIST artist, int page)
        {
            if (page == 1) m_nothingToLoad = false;
            List<TRACK> tracks = new List<TRACK>();
            if (!m_nothingToLoad)
            {
                HtmlDocument doc = new HtmlDocument();
                token.ThrowIfCancellationRequested();
                doc.LoadHtml(await ServiceWebRepository.GetUrlDataAsync(webClient, string.Format(m_url_TOPTRACKSFROMARTIST, artist.artist_key, (page - 1) * m_result_per_page)));
                token.ThrowIfCancellationRequested();
                tracks = GetTracks(doc, token);
            }
            return tracks;
        }

        private bool m_nothingToLoad;
        private List<TRACK> GetTracks(HtmlDocument doc, CancellationToken token)
        {
            List<TRACK> tracks = new List<TRACK>();
            var mainContent = doc.GetElementbyId("mainContentForPage");
            HtmlNode pagedListNode = null;
            if(mainContent != null)
                pagedListNode = mainContent.ChildNodes.First(c => c.Attributes["class"] != null && c.Attributes["class"].Value == "pagedList");
            else
                pagedListNode = doc.DocumentNode.ChildNodes.First(c => c.Attributes["class"] != null && c.Attributes["class"].Value == "pagedList");
            var tracksNode = pagedListNode.ChildNodes.First(c => c.Attributes["class"] != null && c.Attributes["class"].Value == "tracks");

            // Get nothing to load
            var tracklistHeader = tracksNode.ChildNodes.First(c => c.Attributes["class"].Value == "tracklistHeader");
            var pager = tracklistHeader.ChildNodes.FirstOrDefault(c => c.Attributes["class"] != null && c.Attributes["class"].Value == "pager");
            if (pager != null)
                m_nothingToLoad = pager.ChildNodes.FirstOrDefault(c => c.Attributes["class"] != null && c.Attributes["class"].Value == "next") == null;
            else
                m_nothingToLoad = true;

            var tracksTopNode = tracksNode.ChildNodes.First(c => c.Attributes["itemprop"] != null && c.Attributes["itemprop"].Value == "tracks");
            foreach (var node in tracksTopNode.ChildNodes)
            {
                TRACK track = new TRACK();
                track.track_key = node.Attributes["data-track-id"].Value;
                track.web_repo_id = m_web_repository.web_repo_id;
                track.group_key = node.Attributes["data-genre"].Value;

                var heading = node.ChildNodes.First(c => c.Attributes["class"].Value == "heading");

                var title = heading.ChildNodes.First(c => c.Attributes["class"].Value == "title trackTitle");
                track.track_name = HttpUtility.HtmlDecode(title.ChildNodes[0].ChildNodes.First(c => c.Attributes["itemprop"].Value == "name").InnerText);

                var actions = heading.ChildNodes.First(c => c.Attributes["class"].Value == "actions");
                track.track_mp3_url = actions.ChildNodes[1].ChildNodes[0].Attributes["href"].Value.Replace("https", "http");

                var body = node.ChildNodes.First(c => c.Attributes["class"].Value == "body");

                var cover = body.ChildNodes.First(c => c.Attributes["class"].Value == "coverImage");
                track.track_img_url = cover.ChildNodes[0].ChildNodes[0].Attributes["src"].Value;

                var details = body.ChildNodes.First(c => c.Attributes["class"].Value == "details");
                var releaseDate = details.ChildNodes.First(c => c.InnerHtml.Contains("Release Date"));
                track.track_publish_date = DateTime.ParseExact(releaseDate.InnerHtml.Replace("Release Date: ", ""), "yyyy-MM-dd", null);

                track = ServiceWebRepository.InsertOrUpdateTrack(track);

                ARTIST original = new ARTIST();
                var artist = details.ChildNodes.First(c => c.HasAttributes && c.Attributes["class"].Value == "artistName");
                original.artist_name = HttpUtility.HtmlDecode(artist.InnerText);
                original.artist_key = artist.Attributes["href"].Value.Split('/')[2];
                original.web_repo_id = m_web_repository.web_repo_id;

                original = ServiceWebRepository.InsertOrUpdateArtist(original);

                var jn_artist_original_track = new JN_ARTIST_TRACK()
                {
                    artist_key = original.artist_key,
                    track_key = track.track_key,
                    web_repo_id = m_web_repository.web_repo_id,
                    type_artist_id = ServiceWebRepository.GetRefTypeArtist(TypeArtist.Original).type_artist_id
                };

                jn_artist_original_track = ServiceWebRepository.InsertJnArtistTrack(jn_artist_original_track);
                jn_artist_original_track.REF_TYPE_ARTIST = ServiceWebRepository.GetRefTypeArtist(TypeArtist.Original);
                jn_artist_original_track.ARTIST = original;
                track.JN_ARTIST_TRACK.Add(jn_artist_original_track);

                artist = details.ChildNodes.FirstOrDefault(c => c.HasAttributes && c.Attributes["class"].Value == "remixerName");
                if (artist != null)
                {
                    ARTIST remixer = new ARTIST();
                    remixer.artist_name = HttpUtility.HtmlDecode(artist.InnerText);
                    remixer.artist_key = artist.Attributes["href"].Value.Split('/')[1];
                    remixer.web_repo_id = m_web_repository.web_repo_id;

                    remixer = ServiceWebRepository.InsertOrUpdateArtist(remixer);

                    var jn_artist_remixer_track = new JN_ARTIST_TRACK()
                    {
                        artist_key = remixer.artist_key,
                        track_key = track.track_key,
                        web_repo_id = m_web_repository.web_repo_id,
                        type_artist_id = ServiceWebRepository.GetRefTypeArtist(TypeArtist.Remixer).type_artist_id
                    };

                    jn_artist_remixer_track = ServiceWebRepository.InsertJnArtistTrack(jn_artist_remixer_track);
                    jn_artist_remixer_track.REF_TYPE_ARTIST = ServiceWebRepository.GetRefTypeArtist(TypeArtist.Remixer);
                    jn_artist_remixer_track.ARTIST = original;
                    track.JN_ARTIST_TRACK.Add(jn_artist_remixer_track);
                }

                tracks.Add(track);

                token.ThrowIfCancellationRequested();
            }
            return tracks;
        }
        #endregion

        #region GROUPS
        public async Task<List<GROUP>> GetGroupsAsync(WebClient webClient, CancellationToken token)
        {
            List<GROUP> groups = new List<GROUP>();
            HtmlDocument doc = new HtmlDocument();
            token.ThrowIfCancellationRequested();
            doc.LoadHtml(await ServiceWebRepository.GetUrlDataAsync(webClient, m_url_GENRES));
            token.ThrowIfCancellationRequested();
            var genre_nav_node = doc.GetElementbyId("genreNav");
            var genres_node = genre_nav_node.ChildNodes.First(child => child.Attributes["class"].Value == "genreLinks music");
            foreach (var node in genres_node.ChildNodes.Where(child => child.Attributes.Contains("data-genre")))
            {
                var group = new GROUP()
                {
                    group_key = node.Attributes["data-genre"].Value,
                    group_name = HttpUtility.HtmlDecode(node.InnerText),
                    web_repo_id = m_web_repository.web_repo_id
                };

                group = ServiceWebRepository.InsertOrUpdateGroup(group);

                groups.Add(group);

                token.ThrowIfCancellationRequested();
            }
            return groups;
        }
        #endregion

        #region ARTISTS

        public async Task<List<ARTIST>> SearchArtist(WebClient webClient, CancellationToken token, string searchTerms, int page)
        {
            List<ARTIST> artists = new List<ARTIST>();
            HtmlDocument doc = new HtmlDocument();
            token.ThrowIfCancellationRequested();
            doc.LoadHtml(await ServiceWebRepository.GetUrlDataAsync(webClient, string.Format(m_url_SEARCHARTISTS, searchTerms, (page - 1) * m_result_per_page)));
            token.ThrowIfCancellationRequested();

            var mainContent = doc.GetElementbyId("mainContentForPage");
            var pagedListNode = mainContent.ChildNodes.Where(c => c.HasAttributes).First(c => c.Attributes["class"].Value == "pagedList");
            var tracksNode = pagedListNode.ChildNodes.Where(c => c.HasAttributes).FirstOrDefault(c => c.Attributes["class"].Value == "tracks");
            if (tracksNode != null)
            {
                var tracksTopNode = tracksNode.ChildNodes.First(c => c.Attributes["itemprop"] != null && c.Attributes["itemprop"].Value == "tracks");

                List<string> ids = new List<string>();
                foreach (var node in tracksTopNode.ChildNodes)
                {
                    var body = node.ChildNodes.First(c => c.Attributes["class"].Value == "body");

                    var details = body.ChildNodes.First(c => c.Attributes["class"].Value == "details");

                    var artist = details.ChildNodes.First(c => c.HasAttributes && c.Attributes["class"].Value == "artistName");
                    var id = artist.Attributes["href"].Value.Split('/')[2];
                    if (!ids.Contains(id))
                    {
                        ARTIST a = new ARTIST();
                        a.artist_name = HttpUtility.HtmlDecode(artist.InnerText);
                        a.artist_key = id;
                        a.web_repo_id = m_web_repository.web_repo_id;
                        ids.Add(id);
                        artists.Add(a);
                    }
                }
            }

            return artists;
        }
        #endregion
    }
}
